package com.zyk.plugin;

import com.zyk.Environment;
import com.zyk.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ServiceLoader;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

public class PluginManager {
    private static final Logger LOGGER = LoggerFactory.getLogger(PluginManager.class);
    private static final String PLUGIN_ENTRY_NAME = "JA-Plugin-Entry";


    private final List<PluginEntry> plugins = new ArrayList<>();

    public void loadPlugin(PluginEntry pluginEntry) {
        LOGGER.debug("load plugin {}", pluginEntry.getName());
        pluginEntry.init(Environment.getInstance());
        Environment.getInstance().getDispatcherTransformer()
                .addTransformers(pluginEntry.getJaTransformers());
        plugins.add(pluginEntry);
    }

    public void loadPlugins(Instrumentation inst) {
        loadInternalPlugins();
        loadExternalPlugins(inst);
    }

    private void loadInternalPlugins() {
        ServiceLoader<PluginEntry> loader = ServiceLoader.load(PluginEntry.class);
        for (PluginEntry plugin : loader) {
            loadPlugin(plugin);
        }
    }

    /**
     * load plugin from External jar
     */
    private void loadExternalPlugins(Instrumentation inst) {
        File pluginFolder = Environment.getInstance().getPluginFolder();
        if (!pluginFolder.exists() || !pluginFolder.isDirectory())
            return;
        File[] pluginFiles = pluginFolder.listFiles((d, n) -> n.endsWith(".jar"));
        if (null == pluginFiles)
            return;
        for (File pluginJar : pluginFiles) {
            try (JarFile jarFile = new JarFile(pluginJar)) {
                Manifest manifest = jarFile.getManifest();
                String className = manifest.getMainAttributes().getValue(PLUGIN_ENTRY_NAME);
                if (StringUtils.isEmpty(className))
                    return;
                inst.appendToSystemClassLoaderSearch(jarFile);

                Class<?> clazz = Class.forName(className);
                if(!Arrays.asList(clazz.getInterfaces()).contains(PluginEntry.class)) {
                    LOGGER.error("load external plugin failed, error plugin entry. jar: {}", pluginJar.getName());
                }
                loadPlugin((PluginEntry) clazz.newInstance());
            } catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
                LOGGER.error("load external plugin failed.", e);
            }
        }
    }

    public List<PluginEntry> getPlugins() {
        return plugins;
    }
}
